package org.subside.List;

import org.subside.List.pojo.Person;

import java.util.*;
import java.util.function.ToDoubleFunction;
import java.util.stream.Collector;
import java.util.stream.Collectors;

/**
 * @Author : zhangbo
 * @data : 2024/5/16
 * @description :收集(collect) collect，收集，可以说是内容最繁多、功能最丰富的部分
 * collect主要依赖java.util.stream.Collectors类内置的静态方法。
 */
public class StreamTest7 {
    /**
     * 因为流不存储数据，那么在流中的数据完成处理后，
     * 需要将流中的数据重新归集到新的集合里。toList、toSet和toMap比较常用，
     * 另外还有toCollection、toConcurrentMap等复杂一些的用法
     */

    public static void main(String[] args) {
        /**
         * 1.归集(toList/toSet/toMap)
         */
        List<Integer> list = Arrays.asList(1, 6, 3, 4, 6, 7, 9, 6, 20);
        //toList
        List<Integer> listNew = list.stream().filter(x -> x % 2 == 0).collect(Collectors.toList());
        //toSet 去重
        Set<Integer> set = list.stream().filter(x -> x % 2 == 0).collect(Collectors.toSet());

        List<Person> personList = new ArrayList<Person>();
        personList.add(new Person("Tom", 8900, 23, "male", "New York"));
        personList.add(new Person("Jack", 7000, 25, "male", "Washington"));
        personList.add(new Person("Lily", 7800, 21, "female", "Washington"));
        personList.add(new Person("Anni", 8200, 24, "female", "New York"));
        //toMap
        Map<?, Person> map = personList.stream().filter(p -> p.getSalary() > 8000)
                //TODO P->P 是什么意思  `p -> p` 函数返回的是 `Person` 对象本身，满足条件的对象
                .collect(Collectors.toMap(Person::getName, p -> p));
        System.out.println("toList:" + listNew);
        System.out.println("toSet:" + set);
        System.out.println("toMap:" + map);
//        在这段代码中，`p -> p` 表示一个函数，它接收一个参数 `p`（在这里是一个 `Person` 对象），并返回 `p` 本身。
//        这个函数在 `collect(Collectors.toMap(...))` 方法中被用作收集器，用于将流中的元素收集到一个 `Map` 中。
//        这里的 `Map` 是一个泛型类，其键（key）和值（value）的类型由 `<?, Person>` 指定。`?` 表示键的类型是未知的，
//        因为 `toMap` 收集器的键是通过 `Person::getName` 方法从 `Person` 对象中提取的，所以键的类型是 `String`（`Person::getName`
//        返回的类型）。值的类型是 `Person`，因为 `p -> p` 函数返回的是 `Person` 对象本身。
//        整个 `collect` 表达式的作用是，从 `personList` 这个列表的流中筛选出薪水大于 8000 的 `Person` 对象，
//        并将它们收集到一个 `Map` 中，其中每个 `Person` 对象的名字作为键，`Person` 对象本身作为值。
//        完整的代码应该如下所示：
//        Map<String, Person> map = personList.stream()
//                .filter(p -> p.getSalary() > 8000)
//                .collect(Collectors.toMap(Person::getName, p -> p));
//        注意，这里假设 `Person` 类有一个 `getName` 方法来获取名字，还有一个 `getSalary` 方法来获取薪水。
//        此外，`toMap` 收集器可能会抛出 `IllegalStateException`，如果不同的 `Person` 对象有相同的名字（因为键必须是唯一的）。
//        如果存在这种可能性，你可能需要提供一个合并函数来解决键冲突的问题。
        /**
         * 2.统计(count/averaging)
         * Collectors提供了一系列用于数据统计的静态方法：
         * 计数：count
         * 平均值：averagingInt、averagingLong、averagingDouble
         * 最值：maxBy、minBy
         * 求和：summingInt、summingLong、summingDouble
         * 统计以上所有：summarizingInt、summarizingLong、summarizingDouble
         */
        //2.统计员工人数、平均工资、工资总额、最高工资。
        List<Person> personList1 = new ArrayList<Person>();
        personList1.add(new Person("Tom", 8900, 23, "male", "New York"));
        personList1.add(new Person("Jack", 7000, 25, "male", "Washington"));
        personList1.add(new Person("Lily", 7800, 21, "female", "Washington"));
        // 求总数
        Long count = personList1.stream().collect(Collectors.counting());
        Long count1 = personList1.stream().count();
        Long count2 = (long) personList1.size();
        // 求平均工资
        Double average = personList1.stream().collect(Collectors.averagingDouble(Person::getSalary));
        // 求最高工资   compare是一个函数，并将结果转化，返回一个integer类型的结果
        Optional<Integer> max = personList1.stream().map(Person::getSalary).collect(Collectors.maxBy(Integer::compare));
        Optional<Integer> max1 = personList1.stream().map(Person::getSalary).max(Integer::compare);
        // 求工资之和
        Integer sum = personList1.stream().collect(Collectors.summingInt(Person::getSalary));
        Integer sum1 = personList1.stream().mapToInt(Person::getSalary).sum();
        // 一次性统计所有信息
        DoubleSummaryStatistics collect = personList1.stream().collect(Collectors.summarizingDouble(Person::getSalary));
        //    public static <T> Collector<T, ?, DoubleSummaryStatistics> summarizingDouble(ToDoubleFunction<? super T> mapper) {
//        return new CollectorImpl<T, DoubleSummaryStatistics, DoubleSummaryStatistics>(
//                DoubleSummaryStatistics::new,
//                (r, t) -> r.accept(mapper.applyAsDouble(t)),
//                (l, r) -> { l.combine(r); return l; }, CH_ID);
//    }
        System.out.println("员工总数：" + count);
        System.out.println("员工平均工资：" + average);
        System.out.println("员工工资总和：" + sum);
        System.out.println("员工工资所有统计：" + collect);
        /**
         *  3.分组(partitioningBy/groupingBy)
         *  分区：将stream按条件分为两个Map，比如员工按薪资是否高于8000分为两部分。
         * 分组：将集合分为多个Map，比如员工按性别分组。有单级分组和多级分组
         */
        //3.将员工按薪资是否高于8000分为两部分；将员工按性别和地区分组
        List<Person> personList3 = new ArrayList<Person>();
        personList3.add(new Person("Tom",  8900, 18, "male", "New York"));
        personList3.add(new Person("Jack",  7000,19,  "male", "Washington"));
        personList3.add(new Person("Lily", 7800, 20, "female", "Washington"));
        personList3.add(new Person("Anni",  8200,21, "female", "New York"));
        personList3.add(new Person("Owen", 9500, 23, "male", "New York"));
        personList3.add(new Person("Alisa", 7900,27, "female", "New York"));

        // 将员工按薪资是否高于8000分组
        Map<Boolean, List<Person>> part = personList3.stream().collect(Collectors.partitioningBy(x -> x.getSalary() > 8000));
        // 将员工按性别分组
        Map<String, List<Person>> group = personList3.stream().collect(Collectors.groupingBy(Person::getSex));
        // 将员工先按性别分组，再按地区分组
        Map<String, Map<String, List<Person>>> group2 = personList3.stream().collect(Collectors.groupingBy(Person::getSex, Collectors.groupingBy(Person::getArea)));
        System.out.println("员工按薪资是否大于8000分组情况：" + part);
        System.out.println("员工按性别分组情况：" + group);
        System.out.println("员工按性别、地区：" + group2);
        /**
         * 接合(joining)
         * joining可以将stream中的元素用特定的连接符（没有的话，则直接连接）连接成一个字符串。
         */
        List<Person> personList4 = new ArrayList<Person>();
        personList4.add(new Person("Tom", 8900, 23, "male", "New York"));
        personList4.add(new Person("Jack", 7000, 25, "male", "Washington"));
        personList4.add(new Person("Lily", 7800, 21, "female", "Washington"));

        String names = personList4.stream().map(p -> p.getName()).collect(Collectors.joining(","));
        System.out.println("所有员工的姓名：" + names);
        List<String> list1 = Arrays.asList("A", "B", "C");
        String string = list1.stream().collect(Collectors.joining("-"));
        System.out.println("拼接后的字符串：" + string);

        /**
         *  归约(reducing)
         *  Collectors类提供的reducing方法，相比于stream本身的reduce方法，增加了对自定义归约的支持
         */

        // 每个员工减去起征点后的薪资之和（这个例子并不严谨，但一时没想到好的例子）
        Integer sum11 = personList4.stream().collect(Collectors.reducing(0, Person::getSalary, (i, j) -> (i + j - 5000)));
        System.out.println("员工扣税薪资总和：" + sum11);

        // stream的reduce
        Optional<Integer> sum2 = personList.stream().map(Person::getSalary).reduce(Integer::sum);
        System.out.println("员工薪资总和：" + sum2.get());



    }

}
